home *** CD-ROM | disk | FTP | other *** search
- #!/usr/bin/perl5
-
- # Copyright 2002, Silicon Graphics, Inc.
- # All Rights Reserved.
- #
- # This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
- # the contents of this file may not be disclosed to third parties, copied or
- # duplicated in any form, in whole or in part, without the prior written
- # permission of Silicon Graphics, Inc.
- #
- # RESTRICTED RIGHTS LEGEND:
- # Use, duplication or disclosure by the Government is subject to restrictions
- # as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
- # and Computer Software clause at DFARS 252.227-7013, and/or in similar or
- # successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
- # rights reserved under the Copyright Laws of the United States.
-
- ##########################################################################
- #
- # Read and set comments in a GIF file
- #
- # http://www.dcs.ed.ac.uk/home/mxr/gfx/2d/GIF89a.txt
- #
- # Version 1.0 - 12/3/01
- #
- ##########################################################################
-
- # Global array to store existing comment information
- @g_comments = ();
-
- undef $new_comment;
- $gif_file = '';
-
- while($arg = shift(@ARGV)) {
- if($arg eq '-c') {
- $new_comment = shift(@ARGV);
- } elsif($arg eq '-h') {
- ShowUsage();
- exit;
- } elsif($arg =~ m#^-#) {
- print STDERR "Unknown argument '$arg'\n\n";
- ShowUsage();
- exit(1);
- } else {
- $gif_file = $arg;
- last;
- }
- }
-
- if($gif_file eq '') {
- ShowUsage();
- exit(1);
- } elsif(! -e $gif_file) {
- print STDERR "GIF file '$gif_file' doesn't exist.\n\n";
- ShowUsage();
- exit(1);
- }
-
- open(GIF, $gif_file) || die "Can't open '$gif_file'\n";
-
- my(%info) = ReadFileHeader();
-
- SkipColorMap($info{'color_map_size'}) if($info{'global_colormap'} == 1);
-
- my($image_start) = FindImageDescriptorAndComments();
-
- close(GIF);
-
- if(defined($new_comment)) {
- InsertComment($gif_file, $new_comment, $image_start, @g_comments);
- } else {
- ShowComments(@g_comments);
- }
-
- ##########################################################################
- #
- # The Header of the file contains two 3 byte fields.
- # Bytes 0-2 - 'GIF' (GIF file identifier)
- # Bytes 3-5 - version number (either '87a' or '89a')
- #
- # The Logical Screen Descriptor is located directly after the Header.
- # It is a seven byte block orginized as:
- #
- # Bytes 0 & 1 - width (16 bit little-endian)
- # Bytes 2 & 3 - height (16 bit little-endian)
- # Byte 4 - four fields packed together
- # 1 bit - Global Color Table Flag (bit 7)
- # 3 bits - Color Resolution
- # 1 bit - Sort Flag
- # 3 bits - Size of the Global Color Table (bits 0-2)
- # Byte 5 - Background Color Index
- # Byte 6 - Pixel Aspect Ratio
- #
- # Returns a hash table with fields for each piece of information found.
- #
- ##########################################################################
- sub ReadFileHeader {
- my($tmp, $tmp_packed) = '';
- my(%info) = ();
-
- read(GIF, $tmp, 3);
- if($tmp ne 'GIF') {
- die "This is not a GIF file.\n";
- }
-
- read(GIF, $tmp, 3);
- if($tmp ne '87a' && $tmp ne '89a') {
- die "The version '$tmp' is invalid for a GIF file.\n";
- }
-
- $info{'version'} = $tmp;
-
- # height and width are 2 byte (16 bit little-endian) numbers
- read(GIF, $tmp, 2);
- $info{'width'} = unpack("v", $tmp);
-
- read(GIF, $tmp, 2);
- $info{'height'} = unpack("v", $tmp);
-
- read(GIF, $tmp, 1);
- $tmp_packed = unpack("C", $tmp);
-
- $info{'color_map_size'} = 2 << ($tmp_packed & 0x07);
- $info{'sort_flag'} = ($tmp_packed & 0x08) >> 3;
- $info{'color_res'} = (($tmp_packed & 0x70) >> 3) + 1;
- $info{'global_colormap'} = ($tmp_packed & 0x80) >> 7;
-
- read(GIF, $tmp, 1);
- $info{'background'} = unpack("C", $tmp);
-
- read(GIF, $tmp, 1);
- $info{'aspect_ratio'} = unpack("C", $tmp);
-
- return(%info);
- }
-
- ##########################################################################
- #
- # Ignore the portion of the GIF file where the colormap is listed
- #
- # $colormap_size - number of colors described with RGB triplets (3 bytes)
- #
- ##########################################################################
- sub SkipColorMap {
- my($colormap_size) = @_;
-
- # RGB triplets are used to describe each color
- read(GIF, $tmp, 3 * $colormap_size);
- }
-
- ##########################################################################
- #
- # Process the remainder of the file before the image data starts. This
- # is where any comments should be located in the file. According to the
- # GIF spec, comment blocks could occur after the image data, but for the
- # purpose of this program they can be ignored.
- #
- # Returns the location in the file where the image data begins.
- #
- ##########################################################################
- sub FindImageDescriptorAndComments {
- my($image_start) = 0;
- # or set to tell(GIF) right now
-
- while(read(GIF, $c, 1)) {
- $c_ord = ord($c);
- if($c_ord == 0x3B) {
- # GIF terminator (end of file)
- last;
- } elsif($c_ord == 0x21) {
- ProcessExtension();
- next;
- } elsif($c_ord == 0x2C) {
- # Start of the image descriptor
- $image_start = tell(GIF) - 1;
- last;
- } else {
- print "Unknown char [$c][$c_ord]\n";
- next;
- }
- }
- return($image_start)
- }
-
- ##########################################################################
- #
- # Process GIF comment extension blocks and ignore all others. Comments
- # that are found will be added to the @g_comments array as a string with
- # three colon separated fields fields.
- #
- # comment_start_offset:comment_length:comment_text
- #
- ##########################################################################
- sub ProcessExtension {
- my($label, $comment, $comment_segment) = '';
- my($start, $length) = 0;
-
- read(GIF, $label, 1);
-
- if(ord($label) == 0xFE) {
- # Comment extension
- $start = tell(GIF) - 2;
-
- while(GetDataBlock(\$comment_segment)) {
- $comment .= $comment_segment;
- }
-
- $length = tell(GIF) - $start;
-
- push(@g_comments, "$start:$length:$comment");
- } else {
- # Ignore other extension types
-
- # Graphic Control Extension - 0xF9
- # Plain Text Label - 0x01
- # Application Extension - 0xFF
-
- while(GetDataBlock()) {};
- }
- }
-
- ##########################################################################
- #
- # Data blocks begin with a single byte which is the length of the
- # data contained in the block. Any data read will be stored in the
- # $buffer_ref parameter reference passed to this routine. If the
- # incorrect amount of data is read, the program will exit.
- #
- # Returns the number of bytes of data read. 0 will be returned for
- # an empty data block.
- #
- ##########################################################################
- sub GetDataBlock {
- my($buffer_ref) = @_;
-
- my($size) = 0;
-
- read(GIF, $size, 1);
- $size = ord($size);
-
- return(0) if($size == 0);
-
- if(read(GIF, $$buffer_ref, $size) != $size) {
- die "Error reading data block.\n";
- } else {
- return($size);
- }
- }
-
- ##########################################################################
- #
- # Insert a new comment into the GIF file. Any existing comments will
- # be overwritten. The main steps used are:
- # 1. Read the entire GIF file into a string
- # 2. Strip out existing comments
- # 3. Print out the GIF up to the start of the image data
- # 4. Print out the new comment
- # 5. Print the remainder of the image data
- #
- ##########################################################################
- sub InsertComment {
- my($file, $comment, $image_start, @existing_comments) = @_;
-
- open(GIF_IN, "$file") || die "Can't open '$file'\n";
- my($f) = join('', <GIF_IN>);
- close(GIF_IN);
-
- # Remove existing comments
- foreach my $info (reverse(@existing_comments)) {
- my($start, $length, $old_comment) = split(/:/, $info, 3);
- substr($f, $start, $length) = "";
- $image_start -= $length;
- }
-
- open(GIF_OUT, ">$file") || die "Can't write to '$file'\n";
-
- # Print the file to the point where the comment is inserted
- print GIF_OUT substr($f, 0, $image_start);
-
- if($comment ne '') {
- # Add the comment
- # First print comment extension block identifier
- print GIF_OUT pack("c", 0x21);
- print GIF_OUT pack("c", 0xFE);
-
- # Break up comment into maximum 254 byte data block chunks
- while(length($comment) > 254) {
- print GIF_OUT pack("c", 254);
- print GIF_OUT substr($comment, 0, 254);
- $comment = substr($comment, 254);
- }
-
- # Print remainder of the comment
- print GIF_OUT pack("c", length($comment));
- print GIF_OUT $comment;
-
- # Terminating empty data block for the comment
- print GIF_OUT pack("c", 0x00);
- }
-
- # Print the remainder of the image
- print GIF_OUT substr($f, $image_start);
-
- close(GIF_OUT);
- }
-
- ##########################################################################
- #
- # Display any comments found
- #
- ##########################################################################
- sub ShowComments {
- my(@comments) = @_;
-
- foreach my $info (@comments) {
- my($start, $length, $comment) = split(/:/, $info, 3);
- print "$comment\n";
- }
- }
-
- ##########################################################################
- #
- # Display usage information
- #
- ##########################################################################
- sub ShowUsage {
-
- my($name) = $0;
- $name =~ s#^.*/([^/]+)$#$1#;
-
- print <<USAGE;
- Usage: $name [-h] [-c comment] file
-
- -h = Print this usage statement
-
- -c = Specify a comment string to add to the GIF file
-
- With no options, any existing comments will be shown. When a new comment
- is specified, all existing comments will be overwritten.
-
- USAGE
-
- }
-